// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "Subsystems/WorldSubsystem.h"

#include "LyraTeamSubsystem.generated.h"

#define UE_API LYRAGAME_API

class AActor;
class ALyraPlayerState;
class ALyraTeamInfoBase;
class ALyraTeamPrivateInfo;
class ALyraTeamPublicInfo;
class FSubsystemCollectionBase;
class ULyraTeamDisplayAsset;
struct FFrame;
struct FGameplayTag;

/**
 * Lyra团队显示资源变化委托，包含一个参数：显示资源
 */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnLyraTeamDisplayAssetChangedDelegate, const ULyraTeamDisplayAsset*, DisplayAsset);

/**
 * 团队跟踪信息结构体
 */
USTRUCT()
struct FLyraTeamTrackingInfo
{
	GENERATED_BODY()

public:
	UPROPERTY()
	TObjectPtr<ALyraTeamPublicInfo> PublicInfo = nullptr; // 公共团队信息

	UPROPERTY()
	TObjectPtr<ALyraTeamPrivateInfo> PrivateInfo = nullptr; // 私有团队信息

	UPROPERTY()
	TObjectPtr<ULyraTeamDisplayAsset> DisplayAsset = nullptr; // 显示资源

	UPROPERTY()
	FOnLyraTeamDisplayAssetChangedDelegate OnTeamDisplayAssetChanged; // 团队显示资源变化委托

public:
	/**
	 * 设置团队信息
	 */
	void SetTeamInfo(ALyraTeamInfoBase* Info);

	/**
	 * 移除团队信息
	 */
	void RemoveTeamInfo(ALyraTeamInfoBase* Info);
};

// 比较两个角色的团队关联的结果
UENUM(BlueprintType)
enum class ELyraTeamComparison : uint8
{
	// 两个角色都是同一团队的成员
	OnSameTeam, // 同一团队

	// 角色是对立团队的成员
	DifferentTeams, // 不同团队

	// 一个（或两个）角色无效或不属于任何团队
	InvalidArgument // 无效参数
};

/** 用于轻松访问基于团队的角色的团队信息的子系统 */
UCLASS(MinimalAPI)
class ULyraTeamSubsystem : public UWorldSubsystem
{
	GENERATED_BODY()

public:
	UE_API ULyraTeamSubsystem(); // 构造函数

	//~USubsystem接口
	UE_API virtual void Initialize(FSubsystemCollectionBase& Collection) override; // 初始化
	UE_API virtual void Deinitialize() override; // 反初始化
	//~USubsystem接口结束

	// 尝试注册新团队
	UE_API bool RegisterTeamInfo(ALyraTeamInfoBase* TeamInfo); // 注册团队信息

	// 尝试取消注册团队，如果不起作用将返回false
	UE_API bool UnregisterTeamInfo(ALyraTeamInfoBase* TeamInfo); // 取消注册团队信息

	// 如果可能，更改与此角色关联的团队
	// 注意：此函数只能在权限端调用
	UE_API bool ChangeTeamForActor(AActor* ActorToChange, int32 NewTeamId); // 改变角色的团队

	// 返回此对象所属的团队，如果不是团队的一部分则返回INDEX_NONE
	UE_API int32 FindTeamFromObject(const UObject* TestObject) const; // 从对象查找团队

	// 返回此角色的关联玩家状态，如果未与玩家关联则返回INDEX_NONE
	UE_API const ALyraPlayerState* FindPlayerStateFromActor(const AActor* PossibleTeamActor) const; // 从角色查找玩家状态

	// 返回此对象所属的团队，如果不是团队的一部分则返回INDEX_NONE
	UFUNCTION(BlueprintCallable, BlueprintPure=false, Category=Teams, meta=(Keywords="Get"))
	UE_API void FindTeamFromActor(const UObject* TestActor, bool& bIsPartOfTeam, int32& TeamId) const; // 从角色查找团队

	// 比较两个角色的团队，并返回一个值，指示它们是否在同一团队、不同团队，或者一个/两个无效
	UFUNCTION(BlueprintCallable, BlueprintPure=false, Category=Teams, meta=(ExpandEnumAsExecs=ReturnValue))
	UE_API ELyraTeamComparison CompareTeams(const UObject* A, const UObject* B, int32& TeamIdA, int32& TeamIdB) const; // 比较团队

	// 比较两个角色的团队，并返回一个值，指示它们是否在同一团队、不同团队，或者一个/两个无效
	UE_API ELyraTeamComparison CompareTeams(const UObject* A, const UObject* B) const; // 比较团队（简化版）

	// 如果发起者可以对目标造成伤害，则返回true，考虑友军火力设置
	UE_API bool CanCauseDamage(const UObject* Instigator, const UObject* Target, bool bAllowDamageToSelf = true) const; // 是否可以造成伤害

	// 向标签添加指定数量的堆栈（如果StackCount低于1则不执行任何操作）
	UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category=Teams)
	UE_API void AddTeamTagStack(int32 TeamId, FGameplayTag Tag, int32 StackCount); // 添加团队标签堆栈

	// 从标签移除指定数量的堆栈（如果StackCount低于1则不执行任何操作）
	UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category=Teams)
	UE_API void RemoveTeamTagStack(int32 TeamId, FGameplayTag Tag, int32 StackCount); // 移除团队标签堆栈

	// 返回指定标签的堆栈计数（如果标签不存在则返回0）
	UFUNCTION(BlueprintCallable, Category=Teams)
	UE_API int32 GetTeamTagStackCount(int32 TeamId, FGameplayTag Tag) const; // 获取团队标签堆栈计数

	// 如果至少有一个指定标签的堆栈则返回true
	UFUNCTION(BlueprintCallable, Category=Teams)
	UE_API bool TeamHasTag(int32 TeamId, FGameplayTag Tag) const; // 团队是否有标签

	// 如果指定团队存在则返回true
	UFUNCTION(BlueprintCallable, Category=Teams)
	UE_API bool DoesTeamExist(int32 TeamId) const; // 团队是否存在

	// 从指定团队的角度获取指定团队的团队显示资源
	// （您还必须指定查看者，以防游戏模式处于"本地玩家始终是蓝队"的情况）
	UFUNCTION(BlueprintCallable, Category=Teams)
	UE_API ULyraTeamDisplayAsset* GetTeamDisplayAsset(int32 TeamId, int32 ViewerTeamId); // 获取团队显示资源

	// 从指定团队的角度获取指定团队的团队显示资源
	// （您还必须指定查看者，以防游戏模式处于"本地玩家始终是蓝队"的情况）
	UFUNCTION(BlueprintCallable, Category = Teams)
	UE_API ULyraTeamDisplayAsset* GetEffectiveTeamDisplayAsset(int32 TeamId, UObject* ViewerTeamAgent); // 获取有效团队显示资源

	// 获取团队列表
	UFUNCTION(BlueprintCallable, BlueprintPure=false, Category=Teams)
	UE_API TArray<int32> GetTeamIDs() const; // 获取团队ID列表

	// 当团队显示资源被编辑时调用，导致所有团队颜色观察者更新
	UE_API void NotifyTeamDisplayAssetModified(ULyraTeamDisplayAsset* ModifiedAsset); // 通知团队显示资源已修改

	// 为指定团队ID注册团队显示资源通知
	UE_API FOnLyraTeamDisplayAssetChangedDelegate& GetTeamDisplayAssetChangedDelegate(int32 TeamId); // 获取团队显示资源变化委托

private:
	UPROPERTY()
	TMap<int32, FLyraTeamTrackingInfo> TeamMap; // 团队映射

	FDelegateHandle CheatManagerRegistrationHandle; // 作弊管理器注册句柄
};

#undef UE_API // 取消定义UE_API宏